package com.Unidbg_hengdin.ndkdemo;

import com.alibaba.fastjson.util.IOUtils;

import com.github.unidbg.AndroidEmulator;

import com.github.unidbg.Module;

import com.github.unidbg.arm.backend.Unicorn2Factory;

import com.github.unidbg.linux.android.AndroidEmulatorBuilder;
import com.github.unidbg.linux.android.AndroidResolver;
import com.github.unidbg.linux.android.dvm.*;

import com.github.unidbg.linux.android.dvm.array.ArrayObject;
import com.github.unidbg.linux.android.dvm.array.ByteArray;
import com.github.unidbg.linux.android.dvm.array.IntArray;
import com.github.unidbg.memory.Memory;


import java.io.File;

public class callNdkDemo extends AbstractJni {
    private final AndroidEmulator emulator;
    private final VM vm;
    private final Module module;

    private final DvmClass MainActivity;

    private final boolean logging;

    com.Unidbg_hengdin.ndkdemo.NDKDemo ndkdemoObj;
    callNdkDemo(boolean logging) {
        this.logging = logging;

        emulator = AndroidEmulatorBuilder.for64Bit()
                .setProcessName("com.xiaojianbang.ndkdemo")
                .addBackendFactory(new Unicorn2Factory(true))
                .build(); // 创建模拟器实例，要模拟32位或者64位，在这里区分
        final Memory memory = emulator.getMemory(); // 模拟器的内存操作接口
        memory.setLibraryResolver(new AndroidResolver(23)); // 设置系统类库解析

        vm = emulator.createDalvikVM(); // 创建Android虚拟机
        vm.setJni(this);
        vm.setVerbose(logging); // 设置是否打印Jni调用细节
        DalvikModule dm = vm.loadLibrary(new File("unidbg-android/src/test/java/com/Unidbg_hengdin/ndkdemo/libxiaojianbangA.so"), false); // 加载libttEncrypt.so到unicorn虚拟内存，加载成功以后会默认调用init_array等函数
//        DalvikModule dmB = vm.loadLibrary(new File("unidbg-android/src/test/java/com/Unidbg_hengdin/ndkdemo/libxiaojianbangB.so"), false); // 加载libttEncrypt.so到unicorn虚拟内存，加载成功以后会默认调用init_array等函数
        dm.callJNI_OnLoad(emulator); // 手动执行JNI_OnLoad函数
        module = dm.getModule(); // 加载好的libttEncrypt.so对应为一个模块

        MainActivity = vm.resolveClass("com/xiaojianbang/ndkdemo/MainActivity");
    }

    void destroy() {
        IOUtils.close(emulator);
        if (logging) {
            System.out.println("destroy");
        }
    }

    public static void main(String[] args) throws Exception {
        callNdkDemo test = new callNdkDemo(true);

        test.callFunc();

        test.destroy();
    }

    void callFunc() {
        StringObject res = MainActivity.callStaticJniMethodObject(emulator,"testJniFunc()Ljava/lang/String;");
        System.out.println(res.getValue());
    }

    @Override
    public DvmObject<?> callObjectMethodV(BaseVM vm, DvmObject<?> dvmObject, String signature, VaList vaList) {
        if("java/lang/Class->getClassLoader()Ljava/lang/ClassLoader;".equals(signature)){
            return vm.resolveClass("Ljava/lang/ClassLoader;").newObject(null);
        }else if("com/xiaojianbang/ndkdemo/NDKDemo->privateFunc(Ljava/lang/String;I)Ljava/lang/String;".equals(signature)){
            NDKDemo ndkDemo_obj= (NDKDemo) dvmObject.getValue();
            String args0  = (String) vaList.getObjectArg(0).getValue();
            int args1  = vaList.getIntArg(1);
            System.out.println("=======" + ndkDemo_obj+ "==="+ args1);
            return new StringObject(vm,ndkDemo_obj.privateFunc(args0,args1));
        }
        return super.callObjectMethodV(vm, dvmObject, signature, vaList);
    }

    @Override
    public DvmObject<?> allocObject(BaseVM vm, DvmClass dvmClass, String signature) {
//        if("com/xiaojianbang/ndkdemo/NDKDemo->allocObject".equals(signature)){
//            com.xiaojianbang.ndkdemo.NDKDemo ndkdemo = new NDKDemo("test");
//            return vm.resolveClass("com.xiaojianbang.ndkdemo.NDKDemo").newObject(ndkdemo);
//        }
        if("com/xiaojianbang/ndkdemo/NDKDemo->allocObject".equals(signature)){

            return vm.resolveClass("com/xiaojianbang/ndkdemo/NDKDemo").newObject(null);
        }
        return super.allocObject(vm, dvmClass, signature);
    }

    @Override
    public DvmObject<?> newObjectV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
        if ("com/xiaojianbang/ndkdemo/NDKDemo-><init>(Ljava/lang/String;I)V".equals(signature)){
            StringObject args0 = vaList.getObjectArg(0);
            int args1 = vaList.getIntArg(1);
            System.out.println("newObjectV -- " + args0.getValue() + "--" + args1);

            ndkdemoObj = new NDKDemo(args0.getValue(),args1);
            return vm.resolveClass("com.xiaojianbang.ndkdemo.NDKDemo").newObject(ndkdemoObj);
        }
        return super.newObjectV(vm, dvmClass, signature, vaList);
    }

    @Override
    public DvmObject<?> getStaticObjectField(BaseVM vm, DvmClass dvmClass, String signature) {
        if ("com/xiaojianbang/ndkdemo/NDKDemo->privateStaticStringField:Ljava/lang/String;".equals(signature)){
            return new StringObject(vm,ndkdemoObj.privateStaticStringField);
        }
        return super.getStaticObjectField(vm, dvmClass, signature);
    }

    @Override
    public void setObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature, DvmObject<?> value) {
        if("com/xiaojianbang/ndkdemo/NDKDemo->privateStringField:Ljava/lang/String;".equals(signature)){
            System.out.println("com/xiaojianbang/ndkdemo/NDKDemo->privateStringField:Ljava/lang/String; --- :"+ value.getValue());
            NDKDemo ndkDemo_obj= (NDKDemo) dvmObject.getValue();
            ndkDemo_obj.privateStringField = (String)value.getValue();
            return ; 
        }
        super.setObjectField(vm, dvmObject, signature, value);
    }


    @Override
    public DvmObject<?> getObjectField(BaseVM vm, DvmObject<?> dvmObject, String signature) {
        if("com/xiaojianbang/ndkdemo/NDKDemo->privateStringField:Ljava/lang/String;".equals(signature)){
            NDKDemo ndkDemo_obj= (NDKDemo) dvmObject.getValue();
            return new StringObject(vm,ndkDemo_obj.privateStringField);
        }else if("com/xiaojianbang/ndkdemo/NDKDemo->byteArray:[B".equals(signature)){
            NDKDemo ndkDemo_obj= (NDKDemo) dvmObject.getValue();
            return new ByteArray(vm, ndkDemo_obj.byteArray);
        }
        return super.getObjectField(vm, dvmObject, signature);
    }

    @Override
    public void callStaticVoidMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
        if("com/xiaojianbang/ndkdemo/NDKDemo->publicStaticFunc()V".equals(signature)){
             NDKDemo.publicStaticFunc();
             return ;
        }
        super.callStaticVoidMethodV(vm, dvmClass, signature, vaList);
    }

    @Override
    public DvmObject<?> callStaticObjectMethodV(BaseVM vm, DvmClass dvmClass, String signature, VaList vaList) {
        if("com/xiaojianbang/ndkdemo/NDKDemo->privateStaticFunc([Ljava/lang/String;)[I".equals(signature)){
            ArrayObject arrayObject_dvm= vaList.getObjectArg(0);
            DvmObject[] dvmobjects = arrayObject_dvm.getValue();

            String[] arrayObject = new String[dvmobjects.length];

            for (int i = 0; i < dvmobjects.length; i++) {
                arrayObject[i] =(String) dvmobjects[i].getValue();
            }
            return new IntArray(vm,NDKDemo.privateStaticFunc(arrayObject)) ;
        }
        return super.callStaticObjectMethodV(vm, dvmClass, signature, vaList);
    }
}


